package AlienArena::RCon::Console;
use strict;
use warnings;
use POE qw(Session Component::Client::Rcon);
use AlienArena::RCon::Response;
use Carp qw(croak);
$|=1;

our $VERBOSE = $ENV{RCON_DEBUG};
our $CON_ID = 0;
my $response_received;

sub new { shift->init(@_) }

sub init {
    my $class = shift;
    my $self = bless {}, $class;
    $CON_ID++;
    my %args = @_;
    my ($h,$p) = ($args{Host} && ($args{Host} =~ /(.*):([0-9]+)$/));
    $self->host($h || $args{Host});
    $self->port($p || $args{Port});
    $self->pass($args{Pass} || $ENV{AA_PASS});
    $self->timeout($args{Timeout} || 15);
    $self->retry($args{Retry} || 2);
    $self->bytes($args{Bytes} || 8192);
    $self->{_response} = AlienArena::RCon::Response->new($self);

    # parms validation here?

    POE::Kernel->run(); # call first to set "run" flag, but don't run yet.
    POE::Session->create(
	inline_states => {
	    _start => sub { 
		print STDERR "Rcon capture session started\n" if $VERBOSE;
		$_[KERNEL]->alias_set('cmd');
		POE::Component::Client::Rcon->new( 
		    Alias => 'rcon', 
		    Timeout => $self->timeout, 
		    Retry => $self->retry, 
		    Bytes => $self->bytes
		    );
		$_[HEAP]->{consoles}->[$CON_ID] = {}; # con_info
	    },
	    send_command => sub {
		my $cmd = $_[ARG0];
		my $con_info = $_[HEAP]->{consoles}->[$CON_ID];
		$self->response_received(0);
		print STDERR "Sending command\n" if $VERBOSE;
		$_[KERNEL]->post('rcon', 'rcon', 'q2', 
				 $self->host,
				 $self->port,
				 $self->pass, 
				 $cmd, 'response', "con_${CON_ID}");
	    },
	    acknowledge_response => sub {
		$self->response_received(1);
	    },
	    ping => sub { 
		print "AA POE Console $CON_ID\n"; 
	    },
	    logout => sub {
		$_[KERNEL]->yield('_stop');
	    },
	    _stop => sub {
		print STDERR "Rcon capture session finished\n" if $VERBOSE;
	    }
	},
	object_states => [
	    $self->{_response} => {
		response => 'response_handler'
	    }
	]
	);
    $poe_kernel->run_one_timeslice;
    $self->{_kernel} = $poe_kernel;
    $self->{_con_id} = $CON_ID;
    return $self;
}

sub response_received {
    my $self = shift;
    return $self->{_response_received} = shift if @_;
    return $self->{_response_received};
}

# send_command : send a server command
# returns an AlienArena::RCon::Response object containing the svr response

sub send_command {
    my $self = shift;
    my $cmd = shift;
    unless ($self->host && $self->port && $self->pass) {
	croak "Host, port, and password all must be set";
    }
    $self->response_received(0);
    undef $self->{_error};
    $self->kernel->post('cmd','send_command', $cmd);
    $self->kernel->run_one_timeslice until $self->response_received;
    $self->{_error} = $self->response->error;
    $self->response;
}

sub logout {
    my $self = shift;
    $self->kernel->post('cmd','logout');
    $self->kernel->run;
}

sub connected {
    my $self = shift;
    $self->send_command('skill');
    return $self->ok;
}

sub kernel { return shift->{_kernel} }
sub con_id { return shift->{_con_id} }
sub response { return shift->{_response} }
sub response_as_text { return shift->{_response}->as_text }
sub parsed_response { return shift->{_response}->parsed_response }
sub host { 
    my $self = shift;
    return $self->{_host} = shift if @_;
    return $self->{_host};
}
sub port { 
    my $self = shift;
    return $self->{_port} = shift if @_;
    return $self->{_port};
}
sub pass { 
    my $self = shift;
    return $self->{_pass} = shift if @_;
    return $self->{_pass};
}
sub bytes {
    my $self = shift;
    return $self->{_bytes} = shift if @_;
    return $self->{_bytes};
}
sub timeout {
    my $self = shift;
    return $self->{_timeout} = shift if @_;
    return $self->{_timeout};
}
sub retry {
    my $self = shift;
    return $self->{_retry} = shift if @_;
    return $self->{_retry};
}
sub error { shift->{_error} }
sub ok { !shift->{_error} }
sub errmsg { shift->{_response}->errmsg }

1;

=head1 NAME

AlienArena::RCon::Console - OO interface to remote AA servers

=head1 SYNOPSIS

$console = AlienArena::RCon::Console->init( Host => 'alien.org:27901',
 Pass => 'sekr1t');

$console->send_command("startmap ctf3");

$console->logout;

=head1 DESCRIPTION

AA::RCon::Console is a simple OO interface for sending remote commands
to a running Alien Arena server. It is based on
L<POE::Component::Client::Rcon> and uses a POE session to dispatch
commands.

Server responses are parsed by L<AlienArena::RCon::Response>.

The env variable AA_PASS may be set in lieu of providing a 'Pass' argument.

=head1 METHODS

=head2 init()

=head2 response_received

=head2 send_command( $cmd )

=head2 logout

=head2 connected

=head2 kernel

=head2 con_id

=head2 response

=head2 response_as_text

=head2 parsed_repsonse

=head2 host

=head2 port

=head2 pass

=head2 bytes

=head2 timeout

=head2 retry

=head2 error

=head2 ok

=head2 errmsg

=head1 AUTHOR

Mark A. Jensen (maj -at- fortinbras -dot- us)

=head1 SEE ALSO

L<AlienArena::RCon::Response>

=head1 LICENSE

This software may be used, modified, and distributed under the same
terms as Perl itself.

=cut
